//==============================================================================
//
// File: CBrowser.cpp
// Project: Browser
// Author(s): Matthias Van Eeghem (matthias@van-eeghem.com)
// License: See LICENSE in root directory
//
// The core class of the browser. This handles adding and removing of IPs, loading
// of the favorites when the application starts or starting the launcher with the
// correct parameters.
//
//==============================================================================

#include "CBrowser.h"
#include "Forms/ShowSettings/SettingsDialog.h"
#include "Psapi.h"

MainWindow * CBrowser::m_pMainWindow;

// Initialise the browser
void CBrowser::InitialiseSettings(MainWindow * parentWindow)
{
    m_pMainWindow = parentWindow;

    // Needed to read from the registry settings (through the QSettings class)
    QCoreApplication::setOrganizationName("Matthias Van Eeghem");
    QCoreApplication::setOrganizationDomain("http://matthias.van-eeghem.com");
    QCoreApplication::setApplicationName("San Andreas: Multiplayer");

    // Check if a playername was set already
    // If not, prompt the user to change his name
    QSettings settings;
    QString PlayerName = settings.value("PlayerName").value<QString>();

    if(PlayerName.length() == 0)
    {
        // Set the default name to "Player"
        settings.setValue("PlayerName", "Player");

        // And prompt the dialog to change the name
        ShowSettings ShowSettingsDialog(parentWindow);
        ShowSettingsDialog.exec();
    }
}

// Launches the game
void CBrowser::LaunchGame(QString serverIP, QString serverPort, QString playerName)
{
    QSettings settings;
    // Command line (consists of "serverip serverport playername"
    QString CommandLine = serverIP + " " + serverPort + " " + playerName;
    QString InstallDirectory = settings.value("InstallDir").value<QString>() + "\\";
    QString ExePath = InstallDirectory + "GTA_SA.exe";

    // Make sure the install dir is set
    if(InstallDirectory.length() == 0)
    {
        QMessageBox::information(m_pMainWindow, "Failed to start", "Could not start San Andreas: Multiplayer.\nYour GTA SA installation directory hasn't been set yet. Please do so in the settings.");
        return;
    }

    // Make sure GTA_SA.exe exists
    QFile ExeFile(ExePath);
    if(!ExeFile.exists())
    {
        QMessageBox::information(m_pMainWindow, "Failed to start", "Could not start San Andreas: Multiplayer.\nCould not find GTA_SA.exe. Is your installation directory correct?");
        return;
    }

    // Make sure Client.dll exists
    QFile DllFile(InstallDirectory + "\\Client.dll");
    if(!DllFile.exists())
    {
        QMessageBox::information(m_pMainWindow, "Failed to start", "Could not start San Andreas: Multiplayer.\nCould not find Client.dll. Is your installation directory correct?");
        return;
    }

    // Make sure GTA SA isn't already running
    if(CBrowser::IsProcessRunning("GTA_SA.exe"))
    {
        QMessageBox::information(m_pMainWindow, "Failed to start", "Could not start San Andreas: Multiplayer.\nGTA San Andreas is already running. Please close the game first.");
        return;
    }

    // Convert the QStrings to wchar_t strings
    // First we allocate enough space to do so
    wchar_t * wcCommandLine = new wchar_t[CommandLine.length() + 1];
    wchar_t * wcInstallDirectory = new wchar_t[InstallDirectory.length() + 1];
    wchar_t * wcFullExePath = new wchar_t[ExePath.length() + 1];

    // Now call the .toWCharArray() methods
    CommandLine.toWCharArray(wcCommandLine);
    InstallDirectory.toWCharArray(wcInstallDirectory);
    ExePath.toWCharArray(wcFullExePath);

    // Zero terminate the wchar strings
    wcInstallDirectory[InstallDirectory.length()] = 0;
    wcFullExePath[ExePath.length()] = 0;
    wcCommandLine[CommandLine.length()] = 0;

    // Process and startup info
    PROCESS_INFORMATION piLoadee;
    STARTUPINFO siLoadee;
    ZeroMemory(&siLoadee, sizeof(STARTUPINFO));
    siLoadee.cb = sizeof(STARTUPINFO);

    // Create the actual process
    if(CreateProcess(wcFullExePath, wcCommandLine, NULL, NULL, FALSE, NULL, NULL, wcInstallDirectory, &siLoadee, &piLoadee) == 0)
    {
        QMessageBox::information(m_pMainWindow, "Could not start San Andreas: Multiplayer", "Failed to start. Is your installation directory correct?");
        return;
    }

    // Don't forget to delete the strings
    delete[] wcInstallDirectory;
    delete[] wcFullExePath;
    delete[] wcCommandLine;

    CBrowser::InjectModIntoProcess(piLoadee);
}

// Injects Client.dll into the game
// All credits for the below code (in the method) go to the IV:MP team
void CBrowser::InjectModIntoProcess(PROCESS_INFORMATION piLoadee)
{
    // The next part of the function injects
    // The core of the mod
    const char * szLibraryPath = "Client.Dll";

    // Allocate the a block of memory in our target process for the library path
    void * pRemoteLibraryPath = VirtualAllocEx(piLoadee.hProcess, NULL, (strlen(szLibraryPath) + 1), MEM_COMMIT, PAGE_READWRITE);

    // Write our library path to the allocated block of memory
    SIZE_T sBytesWritten = 0;
    WriteProcessMemory(piLoadee.hProcess, pRemoteLibraryPath, (void *)szLibraryPath, (strlen(szLibraryPath) + 1), &sBytesWritten);

    if(sBytesWritten == (strlen(szLibraryPath) + 1))
    {
        // Get the address of the LoadLibraryA function from Kernel32.dll
        FARPROC pfnLoadLibraryA = GetProcAddress(GetModuleHandle(L"Kernel32"), "LoadLibraryA");

        // Create a thread inside the target process to load our library
        HANDLE hThread = CreateRemoteThread(piLoadee.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnLoadLibraryA, pRemoteLibraryPath, 0, NULL);

        if(hThread)
        {
            // Wait for the created thread to end
            WaitForSingleObject(hThread, INFINITE);

            // Close our thread handle
            CloseHandle(hThread);
        }
    }

    // Free the allocated block of memory inside the target process
    VirtualFreeEx(piLoadee.hProcess, pRemoteLibraryPath, sizeof(pRemoteLibraryPath), MEM_RELEASE);
}

// Check if a process is running
bool CBrowser::IsProcessRunning(QString processName)
{
    // Get all running processes
    unsigned long aProcesses[1024], cbNeeded, cProcesses;
    if(!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
        return false;

    cProcesses = cbNeeded / sizeof(unsigned long);

    // Loop through all processes
    for(unsigned int i = 0; i < cProcesses; i++)
    {
        if(aProcesses[i] == 0)
            continue;

        // Get the process name
        HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, aProcesses[i]);
        wchar_t ProcessName[50];
        GetModuleBaseName(hProcess, 0, ProcessName, 50);
        CloseHandle(hProcess);

        // Return true if we have a match
        if(processName == QString::fromWCharArray(ProcessName))
            return true;
    }

    // Nothing found, return false
    return false;
}
